using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Sio.Mdm.Linq
{
    using System.Linq.Expressions;
    using System.Collections.ObjectModel;
    using IQToolkit;
    using System.Reflection;

    public class TableAlias
    {
        public Type EntityType;
        public String LocalName;
        public String OriginalName;
        public Object Instance;
        public object[] Params;
    }

    //iskanje vseh "Filter classov"
    public class FilterNominator : ExpressionVisitor
    {
        int roundtrip = 0;
        List<TableAlias> _filters = new List<TableAlias>();

        protected FilterNominator() { }

        public static List<TableAlias> Nominate(Expression exp)
        {
            var visitor = new FilterNominator();
            visitor.roundtrip = 1;
            visitor.Visit(exp);
            visitor.roundtrip = 2;
            visitor.Visit(exp);
            return visitor._filters;
        }

        protected override Expression VisitMethodCall(MethodCallExpression m)
        {
            if (roundtrip == 1)
            {
                if (m.Method.Name.Equals("Join"))
                {
                    var a0 = ParameterVisitor.FindParameterAlias(m.Arguments[2]);
                    ConstantExpression cex = m.Arguments[0] as ConstantExpression;
                    if (cex != null)
                      LocalVisitConstant(cex, a0);
                    
                    var a1 = ParameterVisitor.FindParameterAlias(m.Arguments[3]);

                    ConstantExpression cex1 = m.Arguments[1] as ConstantExpression;
                    if (cex1 != null)
                        LocalVisitConstant(cex1, a1);
                }
            }
            return base.VisitMethodCall(m);
        }

        protected Expression LocalVisitConstant(ConstantExpression c, string alias)
        {
                if (c.Value == null)
                    return c;
                if (c.Value.GetType().BaseType.Name.Equals("BaseFilter"))
                {
                    if ((_filters.Where(r => r.EntityType.ToString().Equals(c.Type.GetGenericArguments()[0].ToString())).ToList().Count() == 0)
                        ||
                        (_filters.Where(r => r.Instance == c.Value).Count() == 0))
                    {
                        var tableAlias = new TableAlias() { LocalName = String.Format("A{0}", _filters.Count()), OriginalName=alias, Params = ((Sio.Mdm.Entities.BaseFilter)c.Value)._params, Instance = c.Value, EntityType = c.Type.GetGenericArguments()[0] };
                        _filters.Add(tableAlias);
                    }
                }
                return c;
        }

        protected override Expression VisitConstant(ConstantExpression c)
        {
            if (roundtrip == 2)
            {
                if (c.Value == null)
                    return c;
                if (c.Value.GetType().BaseType.Name.Equals("BaseFilter"))
                {
                    LocalVisitConstant(c, "");
                }
                return base.VisitConstant(c);
            }
            else
                return base.VisitConstant(c);

        }
    }

    public class ParameterVisitor : ExpressionVisitor
    {
        private string _alias;

        public static string FindParameterAlias(Expression exp)
        {
            var r = new ParameterVisitor();
            r.Visit(exp);
            return r._alias;
        }

        protected override Expression VisitParameter(ParameterExpression p)
        {
            if (p.Name == null)
                return p;
            if (p.Type.GetInterface(typeof(Sio.Mdm.Entities.IEntity).FullName) != null)
            {
                _alias = p.Name;
            }
            return p;
        }
    }

    public class TypeVisitor : ExpressionVisitor
    {
        Dictionary<string, Sio.Mdm.Entities.EntityFactory.EntityInfo> _types;

        protected TypeVisitor()
        {
            _types = new Dictionary<string, Sio.Mdm.Entities.EntityFactory.EntityInfo>();
        }

        protected TypeVisitor(ref Dictionary<string, Sio.Mdm.Entities.EntityFactory.EntityInfo> types)
            : base()
        {
            _types = types;
        }

        public static Dictionary<string, Sio.Mdm.Entities.EntityFactory.EntityInfo> Find(Expression e)
        {
            var visitor = new TypeVisitor();
            visitor.Visit(e);
            return visitor._types;
        }

        public static bool Find(Expression e, ref Dictionary<string, Sio.Mdm.Entities.EntityFactory.EntityInfo> aliases)
        {
            var visitor = new TypeVisitor(ref aliases);
            visitor.Visit(e);
            return true;
            //return visitor.types;
        }

        protected override Expression VisitParameter(ParameterExpression p)
        {
            if (!_types.ContainsKey(p.Name))
            {
                _types.Add(p.Name, Sio.Mdm.Entities.EntityFactory.GetEntityInfo(p.Type));
            }

            return base.VisitParameter(p);
        }
    }

}
